﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace SharedLib
{
    public static class Win32Helper
    {
        public const int WM_KEYDOWN = 0x0100;
        public const int WM_KEYUP = 0x0101;
        public const int SW_SHOW = 5;

        public const int WM_SETTEXT = 0x000C;

        public const int VK_RETURN = 13;
        public const int VK_ESCAPE = 27;

        [DllImport("user32.dll", EntryPoint = "mouse_event")]
        public static extern void MouseEvent(
            uint dwFlags,
            int dx,
            int dy,
            uint cButtons,
            uint dwExtraInfo
        );

        [DllImport("user32.dll", EntryPoint = "keybd_event")]
        public static extern void KeyboardEvent(
            int bVk,
            int bScan,
            int dwFlags,
            int dwExtraInfo
        );

        [DllImport("user32.dll")]
        public static extern bool SetForegroundWindow(int hWnd);

        [DllImport("user32.dll")]
        public static extern int SetFocus(int hWnd);

        [DllImport("user32.dll")]
        public static extern int ShowWindow(int hWnd, int nCmdShow);

        [DllImport("User32.dll", EntryPoint = "PostMessage")]
        public static extern int PostMessage(
        int hWnd,   //   handle   to   destination   window   
        int Msg,   //   message   
        int wParam,   //   first   message   parameter   
        uint lParam   //   second   message   parameter   
        );

        [DllImport("User32.dll", EntryPoint = "MapVirtualKey")]
        public static extern int MapVirtualKey(
            int wCode,
            int wMapType
            );

        [DllImport("user32.dll", EntryPoint = "SendMessageA")]
        public static extern int SendMessage(int hwnd, int wMsg, int wParam, string lParam);

        [DllImport("user32.dll", EntryPoint = "GetWindow")]
        public static extern int GetWindow(int hwnd, int uCmd);

        [DllImport("user32.dll", EntryPoint = "CloseWindow")]
        public static extern bool CloseWindow(int hwnd);

        [DllImport("user32.dll", EntryPoint = "FindWindow")]
        public extern static int FindWindow(string lpClassName, string lpWindowName);

        [DllImport("user32.dll", EntryPoint = "FindWindowEx")]
        public extern static int FindWindowEx(
            int hWnd1,
            int hWnd2,
            string lpsz1,
            string lpsz2
        );

        [DllImport("user32.dll", EntryPoint = "IsWindowVisible")]
        public static extern bool IsWindowVisible(int hwnd);

        [DllImport("user32")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr i);

        [DllImport("shell32.dll", SetLastError = true)]
        static extern IntPtr CommandLineToArgvW([MarshalAs(UnmanagedType.LPWStr)]string cmd, out int count);
        [DllImport("kernel32.dll")]
        static extern IntPtr LocalFree(IntPtr mem);

        /// <summary>
        /// Returns a list of child windows
        /// </summary>
        /// <param name="parent">Parent of the windows to return</param>
        /// <returns>List of child windows</returns>
        public static List<IntPtr> GetChildWindows(IntPtr parent)
        {
            List<IntPtr> result = new List<IntPtr>();
            GCHandle listHandle = GCHandle.Alloc(result);
            try
            {
                EnumWindowProc childProc = new EnumWindowProc(EnumWindow);
                EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));
            }
            finally
            {
                if (listHandle.IsAllocated)
                    listHandle.Free();
            }
            return result;
        }

        /// <summary>
        /// Callback method to be used when enumerating windows.
        /// </summary>
        /// <param name="handle">Handle of the next window</param>
        /// <param name="pointer">Pointer to a GCHandle that holds a reference to the list to fill</param>
        /// <returns>True to continue the enumeration, false to bail</returns>
        private static bool EnumWindow(IntPtr handle, IntPtr pointer)
        {
            GCHandle gch = GCHandle.FromIntPtr(pointer);
            List<IntPtr> list = gch.Target as List<IntPtr>;
            if (list == null)
            {
                throw new InvalidCastException("GCHandle Target could not be cast as List<IntPtr>");
            }
            list.Add(handle);
            //  You can modify this to check to see if you want to cancel the operation, then return a null here
            return true;
        }

        /// <summary>
        /// Delegate for the EnumChildWindows method
        /// </summary>
        /// <param name="hWnd">Window handle</param>
        /// <param name="parameter">Caller-defined variable; we use it for a pointer to our list</param>
        /// <returns>True to continue enumerating, false to bail.</returns>
        public delegate bool EnumWindowProc(IntPtr hWnd, IntPtr parameter);

        public static string[] SplitArgs(string cmd)
        {
            int count;
            var ret = CommandLineToArgvW(cmd, out count);
            if (ret == IntPtr.Zero)
                throw new ArgumentException("Unable to split argument.");
            try
            {
                var results = new string[count];
                for (int i = 0; i < count; i++)
                    results[i] = Marshal.PtrToStringUni(Marshal.ReadIntPtr(ret, i * IntPtr.Size));
                return results;
            }
            finally
            {
                LocalFree(ret);
            }
        }
    }
}
